﻿using FrameworkCore.Caches;
using Microsoft.Extensions.Caching.Memory;

namespace Infrastructure.Caches;

public class MemoryCacheService : ICacheService
{
    private readonly IMemoryCache _cache;

    public MemoryCacheService(IMemoryCache memoryCache)
    {
        _cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
    }

    public async Task<T> GetAsync<T>(string key)
    {
        return await Task.FromResult(_cache.Get<T>(key)!);
    }

    public async Task SetAsync<T>(string key, T value, TimeSpan absoluteExpirationRelativeToNow)
    {
        _cache.Set(key, value, new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow
        });
        await Task.CompletedTask;
    }

    public async Task<bool> RemoveAsync(string key)
    {
        _cache.Remove(key);
        return await Task.FromResult(true);
    }

    public async Task<bool> ExistsAsync(string key)
    {
        return await Task.FromResult(_cache.TryGetValue(key, out _));
    }

    public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> valueFactory, TimeSpan absoluteExpirationRelativeToNow)
    {
        var result = _cache.Get<T>(key);
        if (result != null)
        {
            return await Task.FromResult(result);
        }

        var value = await valueFactory();
        _cache.Set(key, value, new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow
        });

        return await Task.FromResult(value);
    }

    public async Task<T> GetOrCreateAsync<T>(string key, Func<T> valueFactory, TimeSpan absoluteExpirationRelativeToNow)
    {
        var result = _cache.Get<T>(key);
        if (result != null)
        {
            return await Task.FromResult(result);
        }

        var value = valueFactory();
        _cache.Set(key, value, new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow
        });

        return await Task.FromResult(value);
    }
}
